﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using snat.model;
using System.Threading.Tasks;
using System.IO;

namespace snat.algorithms.network.TFIDF {
    public  class ClusteredTFIDF<T> : IAlgorithm where T : Document {

        private int maxClusterID = 0;

        public Dictionary<String, Term> Terms = new Dictionary<String, Term>();
        public List<T> Documents = new List<T>();

        public void OnMessage(Message m) {
            //Must have been send from and to the same cluster
            if (!m.Recipients.Any(e => (e as ClusterNode).ClusterID == (m.Sender as ClusterNode).ClusterID)) return;

            //create the document using dirty reflection
            T document = (T)Activator.CreateInstance(
                    typeof(T),
                    new object[] { m, Terms, true });
            Documents.Add(document);

            Console.WriteLine("Sender belongs to cluster {0}", (m.Sender as ClusterNode).ClusterID);
            Console.WriteLine(Documents.Count);
        }

        public void Analyze() {
            maxClusterID = NetworkModel.Instance.Nodes.Values.Max<BaseNode>(e => (e as ClusterNode).ClusterID);

            Console.WriteLine("We have {0} documents and {1} clusters", Documents.Count, maxClusterID);

            Console.Write("Populating IDF values...");
            Parallel.ForEach<Term>(Terms.Values,
                term => { term.IDF = Math.Log((double)Documents.Count / (double)term.AppearenceCount); });
            Console.Write("done.\nPopulating IDF values...");
            Dictionary<Term, double> termsDict = Terms.Values.ToDictionary(e => e, e => e.IDF);
            Parallel.ForEach<Document>(Documents,
                document => { document.PopulateTFIDF(termsDict); });
            Console.WriteLine("done.");

            using (StreamWriter output = new StreamWriter("../../../clusteredtfidf.txt")) {
                for (int cluster = 0; cluster <= maxClusterID; cluster++) {
                    Console.WriteLine("Cluster: {0}", cluster);
                    output.WriteLine("Cluster: {0}", cluster);

                    List<T> clusterDocs = Documents.FindAll(e => (NetworkModel.Instance.Nodes[e.Sender.ID] as ClusterNode).ClusterID == cluster);

                    Console.WriteLine("This cluster has {0} documents", clusterDocs.Count);

                    List<T> mostImportantDocuments = clusterDocs.OrderByDescending(e => e.DocumentImportance).ToList().GetRange(0, Math.Min(clusterDocs.Count, 20));
                    foreach (Document d in mostImportantDocuments) {
                        Console.WriteLine(NetworkModel.Instance.Nodes[d.Sender.ID] + "  Document: " + d.ID + ", Importance = " + d.DocumentImportance.ToString("F2"));
                        output.WriteLine("Sender: {0}, Document: {1}, Importance: {2}", NetworkModel.Instance.Nodes[d.Sender.ID], d.ID, d.DocumentImportance.ToString("F2"));
                        int count = 0;
                        foreach (Term t in d.tfidf.Keys.OrderByDescending(e => e.IDF)) {
                            Console.WriteLine(t.word + " : Appearences = " + t.AppearenceCount + ", tfidf = " + d.tfidf[t].ToString("F2"));
                            output.WriteLine("{0} : Appearences: {1}, tfidf: {2}", t.word, t.AppearenceCount, d.tfidf[t].ToString("F2"));
                            if (count++ == 20) break;
                        }
                        Console.WriteLine("-----------");
                        output.WriteLine("-----------");
                    }
                    Console.WriteLine("\n\n");
                    output.WriteLine("\n\n");
                }
            }
        }

        public string Name {
            get { return "TF-IDF"; }
        }

        public void Register() {
            NetworkModel.Instance.MessageProcessed += OnMessage;
            NetworkModel.Instance.TapeEnded += Analyze;
        }

        public void DeRegister() {
            NetworkModel.Instance.MessageProcessed -= OnMessage;
            NetworkModel.Instance.TapeEnded -= Analyze;
        }

    }
}
